#ifndef __WMYSQL_CPP__
#define __WMYSQL_CPP__

#include "wmysql.h"
#include "wmysqlfunc.h"

using namespace WServer::MySQL;
using namespace WMysqlFunc;

void HandleMessage(struct WServer::MsgHandle::wmsgdata* msg)
{
	int cround = 0;
	if (msg->clientindex > maxconn) {
		delete msg;return;
	}
	switch (msg->type) {
		case WMSG_ACCEPTCONNECT:
			clientlist[msg->clientindex].mysql_tid = getCurrentRound();
			wserPrintMsg("set clientindex:%d to mysql_tid:%d\n", msg->clientindex, clientlist[msg->clientindex].mysql_tid);
			delete msg;
			break;
		default:
			cround = clientlist[msg->clientindex].mysql_tid;
			servermsglist[cround].push_back(msg);
			return; //don't delete msg!
	}
}

void* handlemysql(void* args)
{
	wserPrintMsg("handlemysql. pid:%d\n", syscall(SYS_gettid));
	wmsgdata* msg;
	pthread_detach(pthread_self());
	int ret, inc = (long)args;

	while (1) {
		do {
			msg = WServer::MsgQueue::PopMessage(inc);
			if (msg == 0) break;
			try {
				byteArrays ba(msg->type, msg->getparam(), msg->paramlen());
				switch (msg->type) {
					case WMSG_TIMEOUT: {
						MsgTimeout msgtimeout;msgtimeout.decodeMsgClass(ba);msg_timeout(msgtimeout.timeoutid, &msgtimeout);
						break;
					}
					case WMSG_LOGINATTEMPT: {
						MsgLoginAttempt msgloginattempt;msgloginattempt.decodeMsgClass(ba);msg_loginattempt(msg->clientindex, &msgloginattempt);
						break;
					}
				}
			}
			catch (ByteArrayException& e) {
				e.printdata();
			}
		}
		while (true);
		usleep(10);
	}

	return 0;
}

struct CSaveData {
	int dbid;
	std::string tname;
	std::string cmd;
	CSaveData() {dbid = -1;tname = "";cmd = "";}
	CSaveData(int d, std::string t, std::string c) {dbid = d;tname = t;cmd = c;}
};

struct CPlayerData {
	int clientindex;
	CSaveData base;
	std::deque<CSaveData> extlist;
	bool binit;
	pthread_mutex_t plock;
	CPlayerData() {clientindex = -1;binit = false;}
};

struct CPlayerStruct {
	std::string basename;
	std::vector<std::string> extname;
} globalPlayerStruct;

CPlayerData* playerdata;

void initPlayerData(int clientindex, int dbid)
{
	wserPrintMsg("initPlayerData.clientindex:%d dbid:%d\n", clientindex, dbid);
	if (clientindex < 0 || clientindex > maxconn) return;
	CPlayerData& pd = playerdata[clientindex];
	pd.base.dbid = dbid;
	pd.base.tname = globalPlayerStruct.basename;
	if (pd.base.dbid == -1) return;
	pthread_mutex_lock(&pd.plock);
	char filename[MAX_SIZE], header[MAX_SIZE];int savefile;time_t wsertime;time(&wsertime);tm* wsertm = localtime(&wsertime);
	sprintf(filename, "mysqlupdate/player_%d.sql", pd.base.dbid);sprintf(header, "[%04d/%02d/%02d %02d:%02d:%02d] ", wsertm->tm_year+1900, wsertm->tm_mon+1, wsertm->tm_mday, wsertm->tm_hour, wsertm->tm_min, wsertm->tm_sec);
	savefile = open(filename, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	pd.binit = true;
	if (GData::mysqlsaveplayerdata == 1) {
		write(savefile, header, strlen(header));
		char initcmd[MAX_SIZE];sprintf(initcmd, "INIT player.\n");
		write(savefile, initcmd, strlen(initcmd));
	}
	close(savefile);
	addCallback(clientindex, TIMER_SAVEDATA);
	pthread_mutex_unlock(&pd.plock);
}

void updatePlayerData(int clientindex, std::string tname, int dbid, std::string cmd)
{
	if (clientindex < 0 || clientindex > maxconn) return;
	CPlayerData& pd = playerdata[clientindex];
	if (!pd.binit) return;
	if (pd.base.dbid == -1) return;
	pthread_mutex_lock(&pd.plock);
	char filename[MAX_SIZE], header[MAX_SIZE];int savefile;time_t wsertime;time(&wsertime);tm* wsertm = localtime(&wsertime);
	sprintf(filename, "mysqlupdate/player_%d.sql", pd.base.dbid);sprintf(header, "[%04d/%02d/%02d %02d:%02d:%02d] ", wsertm->tm_year+1900, wsertm->tm_mon+1, wsertm->tm_mday, wsertm->tm_hour, wsertm->tm_min, wsertm->tm_sec);
	savefile = open(filename, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	if (tname == "") {
		if (dbid == pd.base.dbid) {
			CSaveData& sd = pd.base;
//			sd.dbid = dbid;
			if (GData::mysqlsaveplayerdata == 1) {
				sd.cmd = cmd;
				write(savefile, header, strlen(header));
				write(savefile, pd.base.cmd.c_str(), pd.base.cmd.length());
				write(savefile, "\n", 1);
			}
		}
	}
	else {
		for (int i = 0;i < pd.extlist.size();i++) {
			if (pd.extlist[i].tname != tname && pd.extlist[i].dbid == dbid) {
				pd.extlist[i].cmd = cmd;
				if (GData::mysqlsaveplayerdata == 1) {
					write(savefile, header, strlen(header));
					write(savefile, pd.extlist[i].cmd.c_str(), pd.extlist[i].cmd.length());
					write(savefile, "\n", 1);close(savefile);
				}
			}
		}
	}
	close(savefile);
	pthread_mutex_unlock(&pd.plock);
}

void insertPlayerData(int clientindex, std::string tname, int dbid)
{
	if (clientindex < 0 || clientindex > maxconn) return;
	CPlayerData& pd = playerdata[clientindex];
	if (!pd.binit) return;
	if (pd.base.dbid == -1) return;
	pthread_mutex_lock(&pd.plock);
	char filename[MAX_SIZE], header[MAX_SIZE];int savefile;time_t wsertime;time(&wsertime);tm* wsertm = localtime(&wsertime);
	sprintf(filename, "mysqlupdate/player_%d.sql", pd.base.dbid);sprintf(header, "[%04d/%02d/%02d %02d:%02d:%02d] ", wsertm->tm_year+1900, wsertm->tm_mon+1, wsertm->tm_mday, wsertm->tm_hour, wsertm->tm_min, wsertm->tm_sec);
	savefile = open(filename, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	if (tname == "") {
	}
	else {
		bool bfind = false;
		for (int i = 0;i < pd.extlist.size();i++) {
			if (pd.extlist[i].dbid == dbid && pd.extlist[i].tname == tname) {bfind = true;break;}
		}
		if (!bfind) {
			pd.extlist.push_back(CSaveData(dbid, tname, ""));
			if (GData::mysqlsaveplayerdata == 1) {
				write(savefile, header, strlen(header));
				char inscmd[MAX_SIZE];sprintf(inscmd, "INSERT INTO %s WHERE dbid=%d\n", tname.c_str(), dbid);
				write(savefile, inscmd, strlen(inscmd));
			}
		}
	}
	close(savefile);
	pthread_mutex_unlock(&pd.plock);
}

void savePlayerData(int clientindex)
{
	if (clientindex < 0 || clientindex > maxconn) return;
	CPlayerData& pd = playerdata[clientindex];
	if (!pd.binit) return;
	if (pd.base.dbid == -1) return;
	pthread_mutex_lock(&pd.plock);
	char filename[MAX_SIZE], header[MAX_SIZE];int savefile;time_t wsertime;time(&wsertime);tm* wsertm = localtime(&wsertime);
	sprintf(filename, "mysqlupdate/player_%d.sql", pd.base.dbid);sprintf(header, "[%04d/%02d/%02d %02d:%02d:%02d] ", wsertm->tm_year+1900, wsertm->tm_mon+1, wsertm->tm_mday, wsertm->tm_hour, wsertm->tm_min, wsertm->tm_sec);
	savefile = open(filename, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

	ExecuteSQLCmd(pd.base.cmd.c_str());
	pd.base.cmd = "";
	for (int i = 0;i < pd.extlist.size();i++) {
		ExecuteSQLCmd(pd.extlist[i].cmd.c_str());
		pd.extlist[i].cmd = "";
	}
	if (GData::mysqlsaveplayerdata == 1) {
		write(savefile, header, strlen(header));
		char savecmd[MAX_SIZE];sprintf(savecmd, "SAVE player.\n");
		write(savefile, savecmd, strlen(savecmd));
	}
	close(savefile);
	pthread_mutex_unlock(&pd.plock);
}

void removePlayerData(int clientindex)
{
	if (clientindex < 0 || clientindex > maxconn) return;
	CPlayerData& pd = playerdata[clientindex];
	savePlayerData(clientindex);
	if (pd.base.dbid == -1) return;
	pthread_mutex_lock(&pd.plock);
	char filename[MAX_SIZE], header[MAX_SIZE];int savefile;time_t wsertime;time(&wsertime);tm* wsertm = localtime(&wsertime);
	sprintf(filename, "mysqlupdate/player_%d.sql", pd.base.dbid);sprintf(header, "[%04d/%02d/%02d %02d:%02d:%02d] ", wsertm->tm_year+1900, wsertm->tm_mon+1, wsertm->tm_mday, wsertm->tm_hour, wsertm->tm_min, wsertm->tm_sec);
	savefile = open(filename, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	pd.base.dbid = -1;pd.base.cmd = "";
	pd.extlist.clear();
	pd.binit = false;
	if (GData::mysqlsaveplayerdata == 1) {
		write(savefile, header, strlen(header));
		char removecmd[MAX_SIZE];sprintf(removecmd, "REMOVE player.\n");
		write(savefile, removecmd, strlen(removecmd));
	}
	close(savefile);
	cancelCallBackPlayer(clientindex);
	pthread_mutex_unlock(&pd.plock);
}

int main(int argc, char *argv[])
{
	moduleindex = MODULE_WMYSQL;
	wserInit(moduleindex);

	pthread_t pid;
	playerdata = new CPlayerData[maxconn];
	for (int i = 0;i < maxconn;i++) {
		pthread_mutex_init(&playerdata[i].plock, 0);
	}

	wserSection sect("config/mysql/player.xml");
	globalPlayerStruct.basename = sect.GetString("base|TName");
	wserSectionNode* node = sect.GetSubNode("ext")->child;
	while (node) {
		std::string name = sect.GetString("ext|"+node->getkeyname()+"|TName");
		globalPlayerStruct.extname.push_back(name);
		node = node->sibling;
	}

	mysqllock = new pthread_mutex_t[GData::mysqlconnectioncount];
	for (int i = 0;i < GData::mysqlconnectioncount;i++) {
		if (!ConnectDatabase(GData::dbhost.c_str(), GData::dbusername.c_str(), GData::dbpassword.c_str(), GData::dbname.c_str())) {
			return 0;
		}
		pthread_mutex_init(&mysqllock[i], 0);
	}

	int* portlist = new int[GData::handlemysqlcount];
	for (int i = 0;i < GData::handlemysqlcount;i++) portlist[i] = i;
	for (int i = 0;i < GData::handlemysqlcount;i++) {
		pthread_create(&pid, 0, handlemysql, (void*)(intptr_t)portlist[i]);
		sleep(1);
	}
	delete[] portlist;
	while (1) {
		sleep(60);
		wserPrintMsg("MemoryPool.free:%d\n", MemoryPool::instance()->getFreeCount());
	}
	for (int i = 0;i < GData::mysqlconnectioncount;i++) {
		pthread_mutex_destroy(&mysqllock[i]);
	}
	CloseDB();
	for (int i = 0;i < maxconn;i++) {
		pthread_mutex_destroy(&playerdata[i].plock);
	}

	return EXIT_SUCCESS;
}


namespace WServer {
namespace MySQL {

std::vector<MYSQL> mysql;
pthread_mutex_t* mysqllock;

bool ConnectDatabase(const char* host, const char* user, const char* password, const char* db)
{
	MYSQL ms;
	if (mysql_init(&ms) == NULL) {
		wserPrintErr("Can't initialize mysql. \n");
		return false;
	}

	if (mysql_real_connect(&ms, host, user, password, db, 0, NULL, CLIENT_MULTI_RESULTS) == NULL) {
		wserPrintErr("Can't connect mysql database. \n");
		return false;
	}
	mysql.push_back(ms);
	return true;
}

int findFreeMysql()
{
	while (true) {
		for (int i = 0;i < GData::mysqlconnectioncount;i++) {
			if (pthread_mutex_trylock(&mysqllock[i]) != 0) continue;
			return i;
		}
	}
	return -1;
}

void releaseMysql(int inc)
{
	pthread_mutex_unlock(&mysqllock[inc]);
}


bool CloseDB()
{
	for (int i = 0;i < mysql.size();i++) mysql_close(&mysql[i]);
}

WRecordset* ExecuteQueryCmd(const char* sqlcommand)
{
	int inc = findFreeMysql();
	WRecordset* recordset = new WRecordset;

	char head[7];bool bselect = false;
	if (strlen(sqlcommand) > 6) {
		strncpy(head, sqlcommand, 6);
		head[6] = '\0';
		if (strcmp(head, "SELECT") == 0) bselect = true;
	}
	if (!bselect && GData::mysqlquerylog == 1) {
		wserPrintMsg("ExecuteQueryCmd.inc:%d %s\n", inc, sqlcommand);
	}

	if (mysql_query(&mysql[inc], sqlcommand)) {
		int _errno = mysql_errno(&mysql[inc]);
		wserPrintErr("run sqlcommand error! msg:%s errno:%d\n", mysql_error(&mysql[inc]), _errno);
		if (_errno == 2006 || _errno == 2013) {
//			CloseDB(inc);
//			ConnectDatabase(inc, GData::dbhost.c_str(), GData::dbusername.c_str(), GData::dbpassword.c_str(), GData::dbname.c_str());
			if (mysql_query(&mysql[inc], sqlcommand)) {
				wserPrintErr("run sqlcommand error! msg:%s errno:%d\n", mysql_error(&mysql[inc]), errno);
			}
			else {
				recordset->SetRecordset(&mysql[inc]);
			}
		}
	}
	else {
		// quesy success
		recordset->SetRecordset(&mysql[inc]);
	}
	releaseMysql(inc);
	return recordset;
}

bool ExecuteSQLCmd(const char* sqlcommand)
{
	int inc = findFreeMysql();
	char head[7];bool bselect = false;
	if (strlen(sqlcommand) > 6) {
		strncpy(head, sqlcommand, 6);
		head[6] = '\0';
		if (strcmp(head, "SELECT") == 0) bselect = true;
	}
	if (!bselect && GData::mysqlquerylog == 1) {
		wserPrintMsg("ExecuteSQLCmd.inc:%d %s\n", inc, sqlcommand);
	}

	if (mysql_query(&mysql[inc], sqlcommand)) {
		int _errno = mysql_errno(&mysql[inc]);
		wserPrintErr("run sqlcommand error! msg:%s errno:%d\n", mysql_error(&mysql[inc]), _errno);
		if (_errno == 2006 || _errno == 2013) {
//			CloseDB(inc);
//			ConnectDatabase(inc, GData::dbhost.c_str(), GData::dbusername.c_str(), GData::dbpassword.c_str(), GData::dbname.c_str());
			if (mysql_query(&mysql[inc], sqlcommand)) return false;
		}
	}
	WRecordset* recordset = new WRecordset;
	recordset->SetRecordset(&mysql[inc]);
	FreeRecordset(recordset);
	releaseMysql(inc);
	return true;
}

void FreeRecordset(WRecordset*& record) {
	delete record;
	record = NULL;
}

}
}

#endif //__WMYSQL_CPP__
